package n64;

public class MemoryManager {

	public static int RDRAM[] = new int[1048576*2]; // 8MB -> 2MWORDS
	public static int ROM[];	
	public static int SP_MEM[] = new int[0x2000/4];
	
	public static final int readByte(final int address) {

		final int maskaddr = address & 0xfffffff0;
		final int diff = address - maskaddr;
		final int word = n64.MemoryManager.RDRAM[maskaddr];
		
		// now that we have the word, let us extract the byte we want
		// zero-extended
		final int result = (word & (0xff << ((3-diff)*8))) >>> ((3-diff)*8);
		
		return result;
	}

	public static final int readWord(final int address_w) {

		//long address = address_w;//(long)(address_w & 0x00000000FFFFFFFF) - (0xFFFFFFFF * 0x10000000 * 0x10000000 * 0xFF);
		
	    if(address_w >= 0x00000000 && address_w <= 0x007FFFFF) {
	    	
	    	//int word = n64.MemoryManager.RDRAM[address_w >> 2]; 
	    	
			//System.out.println("\n***Read word: " + String.format("0x%08X", word) + " from address_w: " + String.format("0x%08X", address_w) + "\n");
	    	
	    	return n64.MemoryManager.RDRAM[address_w >> 2];
	    }
	    
	    // RDRAM unused area
	    else if(address_w >= 0x00800000 && address_w <= 0x03EFFFFF)
	    	return 0xffffffff;//(byte)electrical_noise;
	    
	    // RDRAM registers
	    else if(address_w >= 0x03F00000 && address_w <= 0x03F00028)
	    	return 0xffffffff;//IO_read_byte(address_w);

	    else if(address_w >= 0x03F00029 && address_w <= 0x03FFFFFF)
	    	return 0xffffffff;//(byte)electrical_noise;
	    
	    // SP interface
	    else if(address_w >= 0x04000000 && address_w <= 0x04001FFF) {

	    	return n64.MemoryManager.SP_MEM[(address_w - 0x04000000) >> 2];
	    }
	    else if(address_w >= 0x04002000 && address_w <= 0x0400FFFF) {

		    return 0xffffffff;//IO_read_byte(address_w);
	    }

	    else if(address_w >= 0x04010000 && address_w <= 0x040FFFFF)
		    return 0xffffffff;//(byte)electrical_noise;
	    
	    // DP(c) interface
	    else if(address_w >= 0x04100000 && address_w <= 0x041FFFFF)
		    return 0xffffffff;//IO_read_byte(address_w);

	    // DP(s) interface
	    else if(address_w >= 0x04200000 && address_w <= 0x042FFFFF)	    	
		    return 0xffffffff;//IO_read_byte(address_w);

		// MIPS interface
	    else if(address_w >= 0x04300000 && address_w <= 0x043FFFFF)		
		    return 0xffffffff;//IO_read_byte(address_w);

	    // Video interface
	    else if(address_w >= 0x04400000 && address_w <= 0x044FFFFF)	{
	    	
		    return VI.readWord(address_w - 0x04400000);
	    }

	    // Audio interface
	    else if(address_w >= 0x04500000 && address_w <= 0x045FFFFF)
		    return 0xffffffff;//IO_read_byte(address_w);

	    // Peripheral interface
	    else if(address_w >= 0x04600000 && address_w <= 0x046FFFFF)
		    return 0xffffffff;//IO_read_byte(address_w);

	    // RDRAM interface
	    else if(address_w >= 0x04700000 && address_w <= 0x047FFFFF)
		    return 0xffffffff;//IO_read_byte(address_w);

	    // Serial interface
	    else if(address_w >= 0x04800000 && address_w <= 0x048FFFFF)
		    return 0xffffffff;//IO_read_byte(address_w);

	    // Unused
	    else if(address_w >= 0x04900000 && address_w <= 0x04FFFFFF)
		    return 0xffffffff;//(byte)electrical_noise;

	    // Cartridge Domain 2 address_w 1
	    else if(address_w >= 0x05000000 && address_w <= 0x05FFFFFF)	    
	    	return get_rom(address_w);

	    // Cartridge Domain 1 address_w 1
	    else if(address_w >= 0x06000000 && address_w <= 0x067FFFFF)
	    	return get_rom(address_w);

	    // Cartridge Domain 2 address_w 2
	    else if(address_w >= 0x08000000 && address_w <= 0x0FFFFFFF)
	    	return get_rom(address_w);
	    
	    // Cartridge Domain 1 address_w 2
	    else if(address_w >= 0x10000000 && address_w <= 0x1000003F)
	    	return get_rom(address_w);

	    else if(address_w >= 0x10000040 && address_w <= 0x11000000)
	    	return get_rom(address_w);
	    
	    else if(address_w >= 0x11000001 && address_w <= 0x17FFFFFF)
	    	return get_rom(address_w);
	    
	    else if(address_w >= 0x18000000 && address_w <= 0x1F39FFFF)
	    	return get_rom(address_w);

	    else if(address_w >= 0x1F3A0000 && address_w <= 0x1FBFFFFF)	
	    	return get_rom(address_w);

	    // PIF ROM, emulated by "void pif_rom()"
	    else if(address_w >= 0x1FC00000 && address_w <= 0x1FC007BF)
	    	return 0xffffffff;//electrical_noise; // electrical noise

	    // PIF RAM
	    else if(address_w >= 0x1FC007C0 && address_w <= 0x1FC007FF)
	    	return 0xffffffff;//PIFRAM[address_w - 0x1fc007c0];

	    else if(address_w >= 0x1FC00800 && address_w <= 0x1FCFFFFF)
		    return 0xffffffff;//electrical_noise; // electrical noise

	    else if(address_w >= 0x1FD00000 && address_w <= 0x7FFFFFFF)
		    return 0xffffffff;//electrical_noise; // electrical noise

	    // kseg0 (mirror of 0x0000 0000 to 0x1FFF FFFF)
	    else if(address_w >= 0x80000000 && address_w <= 0x9FFFFFFF)
	    	return readWord(address_w - 0x80000000);

	    // kseg1 (mirror of 0x0000 0000 to 0x1FFF FFFF)
	    else if(address_w >= 0xA0000000 && address_w <= 0xBFFFFFFF)
	    	return readWord(address_w - 0xA0000000);

	    // ksseg (TLB mapped)
	    else if(address_w >= 0xC0000000 && address_w <= 0xDFFFFFFF)
	    return 0xffffffff;//get_mapped();
	    
	    // kseg3 (TLB mapped)
	    else if(address_w >= 0xE0000000 && address_w <= 0xFFFFFFFF)
	    return 0xffffffff;//get_mapped();
	    
	    else 
	    	return 0xdeadbeef;
//		return 0x12320019;
	}

	static int get_rom(long address)
	{
	    if((address & 0x0000000001ffffff) < n64.MemoryManager.ROM.length)
	    {
	        return(n64.MemoryManager.ROM[(int)(address&0x0000000001ffffff)>>2]);
	    }
	    else return 0x00000000;//electrical_noise;
	}
	
	public static final int readHalfword(final int address) {
		
		return 0xffff;
	}

	public static void writeHalfword(final int hw, final int address) {

		int address_f;
		
		if(address >= 0x00000000 && address <= 0x1FFFFFFF) {
			
			address_f = address;
			
			final int maskaddr = address_f & 0xfffffff0;
			final int diff = address_f - maskaddr;
			
			System.out.println(diff);
		}
		else if(address >= 0x80000000 && address <= 0x9FFFFFFF) {

			writeHalfword(hw, address - 0x80000000);
		}
		else if(address >= 0xA0000000 && address <= 0xBFFFFFFF) {
		
			writeHalfword(hw, address - 0xA0000000);		
		}
	}

	public static void writeByte(final byte bite, final int address) {

		int address_f;
		
		if(address >= 0x00000000 && address < 0x08000000) {
			
			address_f = address;
			
			final int maskaddr = address_f & 0xfffffff0;
			final int diff = address_f - maskaddr;
			int word = n64.MemoryManager.RDRAM[maskaddr];
			word |= (int) (bite << (3 - diff));

			n64.MemoryManager.RDRAM[maskaddr] = word;			
		}
		else if(address >= 0x80000000 && address < 0x80800000) {
			
			address_f = address - 0x80000000;
			
			final int maskaddr = address_f & 0xfffffff0;
			final int diff = address_f - maskaddr;
			int word = n64.MemoryManager.RDRAM[maskaddr];
			word |= (int) (bite << (3 - diff));

			n64.MemoryManager.RDRAM[maskaddr] = word;
		}
		else if(address >= 0xA0000000 && address < 0xA0800000) {
		
			address_f = address - 0xA0000000;
			
			final int maskaddr = address_f & 0xfffffff0;
			final int diff = address_f - maskaddr;
			int word = n64.MemoryManager.RDRAM[maskaddr];
			word |= (int) (bite << (3 - diff));

			n64.MemoryManager.RDRAM[maskaddr] = word;
		}
 		/*
		final int maskaddr = address & 0xfffffff0;
		final int diff = address - maskaddr;
		int word = RDRAM[maskaddr];
		word |= (int) (bite << (3 - diff));

		RDRAM[maskaddr] = word;*/
	}

	public static void writeWord(final int word, final int address) {
		
		if((address & 0x000001000) == 0x000001000) {
			System.out.println("logged write to fire display array: " + String.format("0x%08X", word) + " @ " + String.format("0x%08X", address));
		}
		/*
		if((address & 0xA004B100) == 0xA004B100) {
			System.out.println("logged write to palette array: " + String.format("0x%08X", word) + " @ " + String.format("0x%08X", address));
		}*/
		
		//System.out.println("writeWord: " + String.format("0x%08X", word) + " to address: " + String.format("0x%08X", address) + "\n");
		
		if(address >= 0x00000000 && address <= 0x007fffff) {
			n64.MemoryManager.RDRAM[address>>2] = word;
		}
		else if(address >= 0x04000000 && address <= 0x04001FFF) {
			n64.MemoryManager.SP_MEM[(address - 0x04000000)>>2] = word;
	    }
	    // Video interface
	    else if(address >= 0x04400000 && address <= 0x044FFFFF) {	 
		    VI.writeWord(word, address - 0x04400000);
	    }
		else if(address >= 0x80000000 && address <= 0xBFFFFFFF) {

			writeWord(word,address & 0x0FFFFFFF);
			
			//n64.MemoryManager.RDRAM[(address-0x80000000)>>2] = word;			
		}
		else {
			
			//System.out.println("Can't fulfill writeWord to address: " + String.format("0x%08X", word));
		}
	}
}
